﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FCS
{
    internal class Cache<T, U>
    {
        protected ValCache<U> _cacheItems = new ValCache<U>();

        internal U CacheItem(FunUnary<T, U> fun, T val, int idx)
        {
            U ret = default(U);
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }

        internal U CacheItem(FunUnaryIdx<T, U> fun, T val, int idx)
        {
            U ret = default(U);
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val, idx);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }
    }

    internal class BoolCache<T> : Cache<T, bool>
    {
        internal bool CacheItem(FunPredicate<T> fun, T val, int idx)
        {
            bool ret = false;
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }

        internal bool CacheItem(FunPredicateIdx<T> fun, T val, int idx)
        {
            bool ret = false;
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val, idx);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }

        internal bool CacheItem(FunPredicate2<T> fun, T val, int idx, T val2)
        {
            bool ret = false;
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val, val2);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }

        internal bool CacheItem(FunPredicate2Idx<T> fun, T val, int idx, T val2)
        {
            bool ret = false;
            if (!_cacheItems.TryGetValue(idx, out ret))
            {
                ret = fun(val, idx, val2);
                _cacheItems.AddVal(idx, ret);
            }
            return ret;
        }
    }

    internal class ValCache<U>
    {
        private bool[] _exArray;
        private U[] _valArray;
        private Dictionary<int, U> _valDict;
        private static readonly int ARRAYLEN = 1024;

        internal ValCache()
        {
            _valArray = new U[ARRAYLEN];
            _exArray = new bool[ARRAYLEN]; // all values are false by default
            _valDict = new Dictionary<int, U>();
        }

        internal bool TryGetValue(int idx, out U val)
        {
            if (idx < ARRAYLEN)
            {
                val = _valArray[idx];
                return _exArray[idx];
            }
            else
            {
                return _valDict.TryGetValue(idx, out val);
            }
        }

        internal void AddVal(int idx, U val)
        {
            if (idx < ARRAYLEN)
            {
                _valArray[idx] = val;
                _exArray[idx] = true;
            }
            else
            {
                _valDict[idx] = val;
            }
        }
    }

    internal class SingleValCache<U>
    {
        private bool _ex;
        private U _val;

        internal SingleValCache()
        {
            _ex = false;
        }

        internal bool TryGetValue(out U val)
        {
            val = _val;
            return _ex;
        }

        internal void AddVal(U val)
        {
            _ex = true;
            _val = val;
        }
    }
}
